﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using SLNGP.Common.Extensions;
using SLNGP.Core.GeneticPrograms;
using SLNGP.Core.Individuals;
using SLNGP.Core.Run;

namespace SLNGP.Core.Genes
{
    public abstract class Gene : IGene
    {
        protected Gene()
        {
            this.Children = new ObservableCollection<IGene>();
            Children.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(Children_CollectionChanged);
        }

        void Children_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            foreach (IGene newItem in e.NewItems)
            {
                newItem.ParentGene = this;
            }
        }

        public abstract object Execute(IGeneticProgrammingRun data);
        public abstract string GetStringDisplay();
        public ObservableCollection<IGene> Children { get; protected set; }

        public virtual bool IsValid
        {
            get
            {
                return true;
            }
        }
        public virtual void Fix()
        {
        }

        public void ReplaceWith(IGene newGene)
        {
            newGene.ParentChromosome = this.ParentChromosome;

            newGene.ParentGene = this.ParentGene;

            if (newGene.ParentGene != null)
                newGene.ParentGene.Children.Replace(this, newGene);
        }

        public IGeneticProgram ParentGeneticProgram
        {
            get
            {
                return ParentIndividual.ParentGeneticProgram;
            }
        }
        public IIndividual ParentIndividual
        {
            get
            {
                return ParentChromosome.ParentIndividual;
            }
        }
        public IChromosome ParentChromosome { get; set; }
        private IGene _parentGene;
        public IGene ParentGene
        {
            get { return _parentGene; }
            set
            {
                _parentGene = value;
                if (_parentGene != null)
                {
                    this.ParentChromosome = _parentGene.ParentChromosome;
                }
                else
                {
                    this.ParentChromosome.RootGene = this;
                }
                foreach (var child in Children)
                {
                    child.ParentGene = this;
                }
            }
        }

        public IGeneDefinition GeneDefinition { get; set; }
        public abstract Type GeneReturnType { get; }

        public IGene ChildClone(IGene parentGene, IChromosome parentChromosome)
        {
            IGene NewGene = GeneDefinition.Create();
            NewGene.ParentChromosome = parentChromosome;
            foreach (IGene child in Children)
            {
                NewGene.Children.Add(child.ChildClone(NewGene, parentChromosome));
            }
            return NewGene;
        }

        /// <summary>
        /// Overrides to provide a better debugging exprience in the visual studio visualizer. 
        /// </summary>
        /// <returns>The expression represented by the current gene.</returns>
        public override string ToString()
        {
            return GetStringDisplay();
        }
    }
}